﻿using System;
using System.Text;
using System.Linq;
using System.Linq.Expressions;
using System.Collections.Generic;

using LeanEngine.Data.Linq.Language;
using LeanEngine.Data.Linq.Extension;

namespace LeanEngine.Data.Linq.Translator
{
    internal sealed class OrderbyTranslator : LinqTranslator<OrderbyToken>
    {
        #region Declaration Members

        private QueryContext _context;
        private OrderbyToken _orderList;
        private OrderDirectionType _currentDirection;
        private Stack<MemberExpression> _memberStack = new Stack<MemberExpression>();

        #endregion

        #region LinqTranslator<OrderbyToken> Members

        internal override bool CanEvaluate(QueryContext context)
        {
            switch (context.CurrentMethodCall.Method.Name)
            {
                case "OrderBy":
                case "ThenBy":
                    _currentDirection = OrderDirectionType.ASC;
                    var orderLambdaAsc = GetLambdaWithParamCheck(context.CurrentMethodCall);
                    return (orderLambdaAsc != null);

                case "OrderByDescending":
                case "ThenByDescending":
                    _currentDirection = OrderDirectionType.DESC;
                    var orderLambdaDesc = GetLambdaWithParamCheck(context.CurrentMethodCall);
                    return (orderLambdaDesc != null);

                default:
                    return false;
            }
        }

        internal override OrderbyToken Interpret(QueryContext context)
        {
            this._context = context;
            _orderList = new OrderbyToken();

            this.Evaluate(context.CurrentMethodCall.Arguments[1]);

            return _orderList;
        }

        #endregion

        #region Overrided Method Members

        protected override Expression EvaluateMemberAccess(MemberExpression currentMember)
        {
            // get the selected columns
            if (currentMember.Expression == null || currentMember.Expression.NodeType != ExpressionType.Parameter)
            {
                _memberStack.Push(currentMember);
                base.Evaluate(currentMember.Expression);
            }
            else
            {
                // use the mapping metadata and find the name of this memberInfo in the database
                Type declaringType = currentMember.Member.DeclaringType.Name.Contains("Anonymous") ? currentMember.Type : currentMember.Member.DeclaringType;
                while (declaringType.Name.Contains("Anonymous"))
                {
                    currentMember = _memberStack.Pop();
                    declaringType = currentMember.Member.DeclaringType.Name.Contains("Anonymous") ? currentMember.Type : currentMember.Member.DeclaringType;
                }

                var metaTable = _context.QueryableTableList.Where(declaringType);
                var memberInfo = _memberStack.Count > 0 ? _memberStack.Pop().Member : currentMember.Member;
                var member = metaTable.Columns.Where(c => c.PropertyName == memberInfo.Name).FirstOrDefault();

                if (member == null)
                {
                    bool throwError = true;
                    if (memberInfo != currentMember.Member)
                    {
                        member = metaTable.Columns.Where(c => c.PropertyName == currentMember.Member.Name).FirstOrDefault();
                        throwError = (member == null);
                    }

                    if (throwError) throw new InvalidOperationException(string.Format("The memberInfo {0} in where expression cannot be found on the {1} class", memberInfo.Name, declaringType.Name));
                }


                _orderList.ColumnList.Add(new OrderedColumnToken() { Column = member, Direction = _currentDirection });
            }

            return currentMember;
        }

        #endregion

        #region Method Members
        #endregion
    }
}
